package tool.analyzer
 
import scala.collection.mutable.{HashMap, HashSet}
import tool.analyzer.Types._
object Symbols {
	/** A trait for anything that refers to a symbol. */	

	trait Symbolic[S <: Symbol] {
		self =>

		private var _sym: Option[S] = None

		def setSymbol(sym: S): self.type = {
			_sym = Some(sym)
			this
		}
    
		def getSymbol: S = _sym match {
			case Some(s) => s
			case None => scala.Predef.error("Accessing undefined symbol.")
		} 
	}

	/** Notice that creating a symbol will always automatically assign a unique integer id to it. */
	abstract class Symbol extends Positional {
		val id: Int = ID.next
		val name: String
	}
 
	private object ID {
		private var c: Int = 0

		def next: Int = {
			val ret = c
			c = c + 1
			ret
		}
	}
  
	class GlobalScope {
		var mainClass: ClassSymbol = _
		var classes: HashMap[String, ClassSymbol] = new HashMap[String,ClassSymbol]

		def lookupClass(n: String): Option[ClassSymbol] = classes.get(n)
	}

	class ClassSymbol(val name: String) extends Symbol {
		var parent: Option[ClassSymbol] = None
		var methods = new HashMap[String,MethodSymbol]
		var members = new HashMap[String,VariableSymbol]

		def lookupMethod(n: String): Option[MethodSymbol] = {
			methods.get(n) match{
				case Some(p) => return Some(p)
				case None => 
					parent match{
						case None => None
						case Some(p) => p.lookupMethod(n)
					}
			}
		}

		def lookupVar(n: String): Option[VariableSymbol] = {
		  	members.get(n) match {
		    	case Some(p) => return Some(p)
            	case None => 
              		parent match{
                		case None => None
             		    case Some(p) => p.lookupVar(n)
					}
			}
		}
	}

	class MethodSymbol(val name: String, val classSymbol: ClassSymbol) extends Symbol with Typed {
		var params: HashMap[String,VariableSymbol] = new HashMap[String, VariableSymbol]
		var members: HashMap[String,VariableSymbol] = new HashMap[String, VariableSymbol]

		var exceptionThrow : List[ExceptionSymbol] = Nil
 
		// should contain the same values as the params map, but in the right order.
		var argList: List[VariableSymbol] = Nil

		// first lookups in the parameters, then in the members
		def lookupVar(n: String): Option[VariableSymbol] = {
			var ret = params.get(n)
			if(ret == None)
                ret = members.get(n)
			ret
		}
	}
  
	class VariableSymbol(val name: String) extends Symbol with Typed
    class ExceptionSymbol(name: String) extends VariableSymbol(name)
}
